package com.micro.frog.common.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.beans.BeanMap;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Jason.lv
 * @date 2018/10/12 10:35
 **/
@Slf4j
public class BeanCopyUtil {

    private static final Map<String, BeanCopier> BEAN_COPIER_MAP = new ConcurrentHashMap<>();

    /**
     * bean复制，从source对象复制到target对象
     *
     * @param source 原对象
     * @param target 目标对象
     * @param <T>    原对象类型
     * @param <R>    目标对象类型
     * @throws NullPointerException source或者target为{@code null}
     */
    public static <T, R> void copy(T source, R target) {
        BeanCopier beanCopier = getBeanCopier(source.getClass(), target.getClass());
        beanCopier.copy(source, target, null);
    }

    /**
     * 使用默认无参构造函数创建目标对象，执行复制操作再返回目标对象
     * 构造目标对象使用{@link Class#newInstance()}方法，如果出现异常会返回{@code null}
     *
     * @param source      原对象
     * @param targetClass 目标对象类型
     * @param <T>         原对象类型
     * @param <R>         目标对象类型
     * @return 目标对象
     */
    public static <T, R> R copy(T source, Class<R> targetClass) {
        if (targetClass.isInterface()) {
            throw new IllegalArgumentException("Cannot create an instance for an interface.");
        }
        try {
            R target = targetClass.newInstance();
            copy(source, target);
            return target;
        } catch (InstantiationException | IllegalAccessException e) {
//            log.error("Cannot create an instance for class {}", targetClass.getName(), e);
            return null;
        }
    }

    /**
     * bean复制，从source对象复制到target对象
     *
     * @param sources     原对象列表
     * @param targetClass 目标对象类型
     * @param <T>         原对象类型
     * @param <R>         目标对象类型
     * @throws NullPointerException source或者target为{@code null}
     */
    public static <T, R> List<R> copyList(List<T> sources, Class<R> targetClass) {
        List<R> reList = new ArrayList<>();
        if (sources != null && !sources.isEmpty()) {
            for (T source : sources) {
                reList.add(copy(source, targetClass));
            }
        }
        return reList;
    }

    /**
     * 根据传入的参数，调用{@link Constructor#newInstance(Object...)}创建目标对象，执行复制操作再返回目标对象
     * 如果参数不匹配或者出现异常会返回{@code null}
     *
     * @param source      原对象
     * @param targetClass 目标对象类型
     * @param args        参数列表
     * @param <T>         原对象类型
     * @param <R>         目标对象类型
     * @return 目标对象
     */
    public static <T, R> R copy(T source, Class<R> targetClass, Object... args) {

        if (targetClass.isInterface()) {
            throw new IllegalArgumentException("Cannot create an instance for an interface.");
        }

        // 参数列表为空时，调用无参数的转换方法
        if (args == null || args.length <= 0) {
            return copy(source, targetClass);
        }

        // 识别参数列表的类型
        Class[] parameterTypes = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            parameterTypes[i] = args[i].getClass();
        }
        try {
            // 通过构造函数创建对象
            Constructor<R> constructor = targetClass.getConstructor(parameterTypes);
            R target = constructor.newInstance(args);
            copy(source, target);
            return target;
        } catch (NoSuchMethodException e) {
//            log.error("Cannot create an instance for class {}. Specific constructor {} not exist.",
//                    targetClass.getName(), parameterTypes, e);
        } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
//            log.error("Cannot create an instance for class {}", targetClass.getName(), e);
        }
        return null;
    }

    private static <T, R> BeanCopier getBeanCopier(Class<T> sourceClass, Class<R> targetClass) {
        String key = getKey(sourceClass, targetClass);
        BeanCopier beanCopier = BEAN_COPIER_MAP.get(key);
        if (beanCopier == null) {
            beanCopier = BeanCopier.create(sourceClass, targetClass, false);
            BEAN_COPIER_MAP.put(key, beanCopier);
        }
        return beanCopier;
    }

    private static String getKey(Class sourceClass, Class targetClass) {
        return sourceClass.getName() + targetClass.getName();
    }

    /**
     * 类的实例转为{@link Map}，对象的字段名称作为key，字段的值作为value
     *
     * @param bean 对象
     * @return 包含对象成员变量的{@link Map}
     */
    public static Map<String, Object> beanToMap(Object bean) {
        Map<String, Object> map = new HashMap<>(8);
        if (bean != null) {
            BeanMap beanMap = BeanMap.create(bean);
            for (Object key : beanMap.keySet()) {
                map.put(key.toString(), beanMap.get(key));
            }
        }
        return map;
    }

    /**
     * {@link Map}转为对象，需要指定传入指定类的实例，以{@link Map}的key-value为该实例赋值
     *
     * @param map  包含key-value的{@link Map}
     * @param bean 指定类型的实例
     * @param <T>  指定类型的泛型
     * @return 将{@link Map}转换后的实例
     */
    public static <T> T mapToBean(Map<String, Object> map, T bean) {
        BeanMap beanMap = BeanMap.create(bean);
        beanMap.putAll(map);
        return bean;
    }
}
